part of 'sqliteasy_database.dart';

class TableInfo {
  final String name;
  final Set<ColumnInfo> columns;
  final Set<ForeignKeyInfo> foreignKeys;
  final Set<IndexInfo> indexes;

  TableInfo({this.name, this.columns, this.foreignKeys, this.indexes});

  //TODO: implement the equals method
  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
          other is TableInfo &&
              runtimeType == other.runtimeType &&
              name == other.name &&
              columns.contentEquals(other.columns) &&
              foreignKeys.contentEquals(other.foreignKeys) &&
              indexes.contentEquals(other.indexes);

  @override
  int get hashCode => name.hashCode ^ columns.contentHashCode ^ foreignKeys.contentHashCode ^ indexes.contentHashCode;

  static Future<TableInfo> read(SupportSQLiteDatabase db, String tableName) async {
    // 先判断表是否存在
    final isExisted = await SQLiteUtil.isTableExisted(db, tableName);
    if (!isExisted) {
      return null;
    }
    final tableInfoResult = await Future.wait([
      readColumns(db, tableName),
      readForeignKeys(db, tableName),
      readIndexes(db, tableName),
    ]);
    return TableInfo(
        name: tableName, columns: tableInfoResult[0], foreignKeys: tableInfoResult[1], indexes: tableInfoResult[2]);
  }

  static Future<Set<ColumnInfo>> readColumns(SupportSQLiteDatabase db, String tableName) async {
    List<Map<String, dynamic>> result = await db.exec("PRAGMA table_info(`${tableName}`)");
    final columnInfoList = result.map((e) {
      return ColumnInfo(
        name: e["name"],
        type: SQLiteTypes.getTypeFromName(e["type"]),
        notNull: e["notnull"] == 1,
        defaultValue: e["dflt_value"],
        primaryKeyPosition: e["pk"],
      );
    }).toSet();
    return columnInfoList;
  }

  static Future<Set<ForeignKeyInfo>> readForeignKeys(SupportSQLiteDatabase db, String tableName) async {
    List<Map<String, dynamic>> result = await db.exec("PRAGMA foreign_key_list(`" + tableName + "`)");
    print(result);
    final infoList = result.map((e) {
      return ForeignKeyInfo(
          referenceTable: e["table"],
          onUpdate: Actions.getFromName(e["on_update"]),
          onDelete: Actions.getFromName(e["on_delete"]),
          columns: [e["from"]],
          referenceColumns: [e["to"]]
      );
    }).toSet();
    return infoList;
  }

  static Future<Set<IndexInfo>> readIndexes(SupportSQLiteDatabase db, String tableName) async {
    List<Map<String, dynamic>> result = await db.exec("PRAGMA index_list(`" + tableName + "`)");
    print(result);
    final infoList = HashSet<IndexInfo>();
    for(Map<String, dynamic> e in result) {
      String name = e["name"];
      bool unique = e["unique"] == 1;
      List<Map<String, dynamic>> indexInfo = await db.exec("PRAGMA index_xinfo(`" + name + "`)");
      List<String> columns = indexInfo.map((e) => e["name"]?.toString()).toList()..remove(null);
      infoList.add(IndexInfo(name: name, unique: unique, columns: columns));
      print(indexInfo);
    }
    return infoList;
  }

  @override
  String toString() {
    return 'TableInfo{\nname: $name, \ncolumns: ${columns.join(",\n")}, \nforeignKeys: ${foreignKeys.join("\n")}, \nindexes: ${indexes.join("\n")}\n}';
  }
}

class ColumnInfo {
  String name;
  SQLiteType type;
  bool notNull;
  String defaultValue;
  int primaryKeyPosition;

  ColumnInfo({this.name, this.type, this.notNull, this.defaultValue, this.primaryKeyPosition});

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
          other is ColumnInfo &&
              runtimeType == other.runtimeType &&
              name == other.name &&
              type == other.type &&
              notNull == other.notNull &&
              defaultValue == other.defaultValue &&
              primaryKeyPosition == other.primaryKeyPosition;

  @override
  int get hashCode =>
      name.hashCode ^ type.hashCode ^ notNull.hashCode ^ defaultValue.hashCode ^ primaryKeyPosition.hashCode;

  @override
  String toString() {
    return 'ColumnInfo{\nname: $name, \ntype: $type, \nnotNull: $notNull, \ndefaultValue: $defaultValue, \nprimaryKeyPosition: $primaryKeyPosition\n}';
  }
}

class ForeignKeyInfo {
  String referenceTable;
  List<String> columns;
  List<String> referenceColumns;
  Action onUpdate;
  Action onDelete;

  ForeignKeyInfo({this.referenceTable, this.columns, this.referenceColumns, this.onUpdate, this.onDelete});

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
          other is ForeignKeyInfo &&
              runtimeType == other.runtimeType &&
              referenceTable == other.referenceTable &&
              columns.contentEquals(other.columns) &&
              referenceColumns.contentEquals(other.referenceColumns) &&
              onUpdate == other.onUpdate &&
              onDelete == other.onDelete;

  @override
  int get hashCode =>
      referenceTable.hashCode ^
      columns.contentHashCode ^
      referenceColumns.contentHashCode ^
      onUpdate.hashCode ^
      onDelete.hashCode;

  @override
  String toString() {
    return 'ForeignKeyInfo{\nreferenceTable: $referenceTable, \ncolumns: $columns, \nreferenceColumns: $referenceColumns, \nonUpdate: $onUpdate, \nonDelete: $onDelete\n}';
  }
}

class IndexInfo {
  String name;
  bool unique;
  List<String> columns;

  IndexInfo({this.name, this.unique, this.columns});

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
          other is IndexInfo &&
              runtimeType == other.runtimeType &&
              name == other.name &&
              unique == other.unique &&
              columns.contentEquals(other.columns);

  @override
  int get hashCode => name.hashCode ^ unique.hashCode ^ columns.contentHashCode;

  @override
  String toString() {
    return 'IndexInfo{name: $name, unique: $unique, columns: $columns}';
  }
}
